package com.tattermedia.allthataudio.audio;

import hw.ats.news.model.News;
import hw.ats.news.util.DataLoader;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.StringTokenizer;

import android.database.sqlite.SQLiteDatabase;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaRecorder.AudioSource;
import android.os.Handler;
import android.util.Log;

import com.humanwares.util.HwUtil;

public class AudioPlayerManager implements OnCompletionListener {
	
	public static final int				REPEAT_ALL = 0;
	public static final int				REPEAT_ONE = 1;
	
	public static final String			PLAYLIST_FILE_NAME = "playlist";
	
	
	private static AudioPlayerManager	mInstance;
	
	private String						mPackageName;

	// play list item
	private ArrayList<PlayListItem> 	mPlayListItemArray;
	
	// audio player status listener array
	private ArrayList<IAudioPlayerStatusListener>	mListenerArray;
	
	private MediaPlayer					mMediaPlayer;
	private PlayListItem				mCurrentPlayingItem;
	private Handler						mHandler;
	
	// download %  ( 100 = 100%)
	private int							mDownloadPercent;
	
	private int							mRepeatMode = REPEAT_ALL;
	
	// pause on intrrupt
	private boolean						mInterruptPaused;
	
	private Object						mLock = new Object();
	
	private AudioPlayerManager() {
	}
	
	public static AudioPlayerManager getManager() {
		if (mInstance == null) {
			mInstance = new AudioPlayerManager();
		}
				
		return mInstance;
	}
	
	public void initialize(String packageName) {
		mPlayListItemArray = new ArrayList<PlayListItem>();
		mListenerArray = new ArrayList<IAudioPlayerStatusListener>();
		
		mPackageName = packageName;
		
		mMediaPlayer = new MediaPlayer();
		mHandler = new Handler();
	
		// control update thread
		new Thread(new Runnable(){

			@Override
			public void run() {
				while (true) {
					
					broadcastStatusChanged(IAudioPlayerStatusListener.TYPE_COMMON);
					
					try {
						Thread.sleep(300);
						Thread.yield();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			
		}).start();
	}
	
	public void loadPlayList(SQLiteDatabase db) {
		try {
			FileInputStream fis = new FileInputStream(new File(getAudioStorage() + "/" + PLAYLIST_FILE_NAME));
			
			int size = fis.available();
			byte[] buffer = new byte[size];
			
			fis.read(buffer);
			
			String str = new String(buffer);
			StringTokenizer tokenizer = new StringTokenizer(str, "|");
			
			while (tokenizer.hasMoreTokens()) {
				String token = tokenizer.nextToken();
				
				if (token.trim().length() != 0) {
					
					int uid = Integer.parseInt(token);
					
					News news = News.load(db, uid);
					if (news != null) {
						addToPlayList(news);
					}
					
				}
			}
			
		} catch (FileNotFoundException e) {
		} catch (IOException e) {
		}
	}
	
	public void savePlayList() {
		
		StringBuffer sb = new StringBuffer();
		
		for (PlayListItem item : mPlayListItemArray) {
			sb.append(item.getUid() + "|");
		}
		
		try {
			
			FileOutputStream fos = new FileOutputStream(new File(getAudioStorage() + "/" + PLAYLIST_FILE_NAME));
			fos.write(sb.toString().getBytes());
			fos.close();
			
		} catch (FileNotFoundException e) {
		} catch (IOException e) {
		}
	}
	
	public String getAudioStorage() {
		 boolean sdcardExists = HwUtil.externalStorageExists();
	        if (sdcardExists) {
	        	String path = "/sdcard/" + mPackageName +"/audio";
	        	File sdcardPath = new File(path);
	        	if (!sdcardPath.exists()){
	        		sdcardPath.mkdirs();
	        		
	        		FileOutputStream out;
	        		PrintStream p;
	        		
	        		try {
	        			out = new FileOutputStream(path + "/.nomedia");
	        			p = new PrintStream( out );
	        			p.println("");
	        			p.close();
	     			
	        		}catch(Exception e){
	        			path = "/data/data/" + mPackageName + "/audio";
	        			File file = new File(path);
	        			if (!file.exists()) {
	        				file.mkdirs();
	        				
	        			}
	        			return path;
	        		}
	        	}
	        	return path ;
	        	
	        }
	        
	        String path = "/data/data/" + mPackageName + "/audio";
			File file = new File(path);
			if (!file.exists()) {
				file.mkdirs();
				
			}
			return path;
	}
	
	public void addAudioPlayerStatusListener(IAudioPlayerStatusListener listener) {
		mListenerArray.add(listener);
	}
	
	public void removeAudioPlayerStatusListener(IAudioPlayerStatusListener listener) {
		mListenerArray.remove(listener);
	}
	
	protected void broadcastStatusChanged(final int type) {
		mHandler.post(new Runnable(){
			@Override
			public void run() {
				for (IAudioPlayerStatusListener listener : mListenerArray) {
					listener.onAudioStatusChanged(type);
				}	
			}
		});
	}
	
	public int getPlayListItemCount() {
		return mPlayListItemArray.size();
	}
	
	public boolean isPlaying() {
		synchronized (mLock) {
			if (mMediaPlayer != null) {
				return mMediaPlayer.isPlaying();
			}
			
			return false;
		}
	}
	
	public PlayListItem getPlayListItemFromIndex(int index) {
		
		PlayListItem item = null;
		
		if (index >= 0 && index < mPlayListItemArray.size()) {
			item = mPlayListItemArray.get(index);
		} 
		
		return item;
	}
	
	public PlayListItem getPlayListItemFromUid(int uid) {
		
		for (PlayListItem item : mPlayListItemArray) {
			if (item.getUid() == uid) {
				return item;
			}
		}
		
		return null;
	}
	
	public int indexOf(PlayListItem item) {
		return mPlayListItemArray.indexOf(item);
	}
	
	public int getCurrentDownloadPercent() {
		return mDownloadPercent;
	}
	
	public int getCurrentPosition() {
		synchronized (mLock) {
			if (mCurrentPlayingItem != null) {
				int position = mMediaPlayer.getCurrentPosition();
				
				if (position > mCurrentPlayingItem.getTotalDuration()) {
					position = (int) mCurrentPlayingItem.getTotalDuration();
				}
				
				return position;
			}
			
			return 0;
		}
	}
	
	public int getTotalDuration() {
		if (mCurrentPlayingItem != null) {
			return (int) mCurrentPlayingItem.getTotalDuration();
		}
		
		return 0;
	}
	
	public void seekTo(int position) {
		if (mCurrentPlayingItem != null) {
			synchronized (mLock) {
				mMediaPlayer.seekTo(position);	
			}
		}
	}
	
	public boolean addToPlayList(News news) {
		
		if (!isItemExist(news.uid)) {
			PlayListItem item = new PlayListItem(news.uid, news.title, news.mp3_url, news.mp3_fileSize, news.mp3_totalDuration);
			mPlayListItemArray.add(item);
			
			savePlayList();
			return true;
		}
		
		return false;
	}
	
	public void removeFromPlayList(int uid) {
		for (PlayListItem item : mPlayListItemArray) {
			if (item.getUid() == uid) {
				removeFromPlayList(item);
				break;
			}
		}
	}
	
	public void removeFromPlayList(PlayListItem item) {
		
		if (mCurrentPlayingItem == item) {
			if (isPlaying()) {
				
				if (getPlayListItemCount() == 1) {
					pauseAudio();
					mCurrentPlayingItem = null;
				} else {
					try {
						playNextAudio();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
			} else {
				mCurrentPlayingItem = null;
			}
		}
		
		mPlayListItemArray.remove(item);
		savePlayList();
	}
	
	public void moveUp(PlayListItem item) {
		int currentIndex = mPlayListItemArray.indexOf(item);
		
		if (currentIndex > 0) {
			mPlayListItemArray.remove(currentIndex);
			
			currentIndex -= 1;
			
			mPlayListItemArray.add(currentIndex, item);
			
			savePlayList();
		}
	}
	
	public void moveDown(PlayListItem item) {
		int currentIndex = mPlayListItemArray.indexOf(item);
		
		if (currentIndex < mPlayListItemArray.size() - 1) {
			mPlayListItemArray.remove(currentIndex);
			
			currentIndex += 1;
			
			mPlayListItemArray.add(currentIndex, item);
			
			savePlayList();
		}
	}
	
	public boolean isItemExist(int uid) {
		for (PlayListItem item : mPlayListItemArray) {
			if (uid == item.getUid()) {
				return true;
			}
		}
		
		return false;
	}
	
	public int getCurrentPlayingItemIndex() {
		
		if (mCurrentPlayingItem == null) {
			return -1;
		}
		
		return mPlayListItemArray.indexOf(mCurrentPlayingItem);
	}
	
	public PlayListItem getCurrentPlayingItem() {
		return mCurrentPlayingItem;
	}
	
	public void playAudioFromUid(int uid) throws Exception {
		PlayListItem item = getPlayListItemFromUid(uid);
		
		if (item != null) {
			playAudio(item);
		}
	}
	
	public void playAudioFromIndex(int index) throws Exception {
		PlayListItem item = getPlayListItemFromIndex(index);
		
		if (item != null) {
			playAudio(item);
		}
	}
	
	public void playNextAudio() throws Exception {
		int index = getCurrentPlayingItemIndex();
		++index;
		
		if (index >= getPlayListItemCount()) {
			index = 0;
		}
		
		playAudioFromIndex(index);
	}
	
	public void playPrevAudio() throws Exception {
		int index = getCurrentPlayingItemIndex();
		--index;
		
		if (index < 0) {
			index = getPlayListItemCount() - 1;
		}
		
		playAudioFromIndex(index);
	}
	
	public void pauseAudio() {
		
		synchronized (mLock) {
			mMediaPlayer.pause();
		}
	}
	
	public void resumeAudio() throws Exception {
		if (mCurrentPlayingItem != null) {
			synchronized (mLock) {
				mMediaPlayer.start();	
			}
		} else {
			playAudioFromIndex(0);
		}
	}
	
	public int getRepeatMode() {
		return mRepeatMode;
	}
	
	public void toggleRepeat() {
		if (mRepeatMode == REPEAT_ALL) {
			mRepeatMode = REPEAT_ONE;
		} else {
			mRepeatMode = REPEAT_ALL;
		}
	}
		
	protected void playAudio(final PlayListItem item) throws Exception {
		
		
		String url = item.getUrl();
		String fileName = url.substring(url.lastIndexOf('/') + 1);
		
		String path = getAudioStorage() + "/" + fileName;
		final File cachedFile = new File(path);
		
				
		mCurrentPlayingItem = item;
		
		// to view play list icon on streaming 
		broadcastStatusChanged(IAudioPlayerStatusListener.TYPE_START_PLAY);
		
		if (cachedFile.exists()) {
			if (cachedFile.length() == item.getFileSize()) {
				
				// set download percent
				mDownloadPercent = 100;
				
				// play cached audio file	
				synchronized (mLock) {
					mMediaPlayer.pause();
					mMediaPlayer.release();
					mMediaPlayer = new MediaPlayer();
					mMediaPlayer.setOnCompletionListener(this);
					mMediaPlayer.setDataSource(new FileInputStream(cachedFile).getFD());
					
					mMediaPlayer.prepare();
					mMediaPlayer.start();
				}
				
				broadcastStatusChanged(IAudioPlayerStatusListener.TYPE_START_PLAY);
				return;
			} else {
				
				// file size not match
				cachedFile.delete();
			}
		}
		
		cachedFile.createNewFile();
		
		// start stream play
		mDownloadPercent = 0;
		
		synchronized (mLock) {
			mMediaPlayer.pause();
			mMediaPlayer.seekTo(0);
		}
		
		new Thread(new Runnable(){

			@Override
			public void run() {
				try {
					URLConnection cn = new URL(item.getUrl()).openConnection();   
			        cn.connect();   
			        InputStream inputStream = cn.getInputStream();
			        FileOutputStream outputStream = new FileOutputStream(cachedFile);
			        
			        byte[] buffer = new byte[128 * 1024];
			        long totalFileSize = item.getFileSize();
			        long readedFileSize = 0;
			        			        
			        while(readedFileSize < totalFileSize) {
			        	int size = inputStream.read(buffer);
			        	
			        	outputStream.write(buffer, 0, size);
			        	readedFileSize += size;
			        	
			        	mDownloadPercent = (int)(readedFileSize * 100 / totalFileSize);
			        	
		        	
		        		if (!mMediaPlayer.isPlaying() && readedFileSize > 128 * 1024) {
		        			synchronized (mLock) {
								mMediaPlayer.release();
				        		mMediaPlayer = new MediaPlayer();
				        		mMediaPlayer.setDataSource(new FileInputStream(cachedFile).getFD());
				        		mMediaPlayer.prepare();
								mMediaPlayer.start();
		        			}
									
							
							broadcastStatusChanged(IAudioPlayerStatusListener.TYPE_START_PLAY);
							
			        	} else if ( mMediaPlayer.isPlaying() && 
			        			(readedFileSize == totalFileSize ||
			        				mMediaPlayer.getDuration() - mMediaPlayer.getCurrentPosition() < 1000)) {
			        		
			        		// some issue buffer change delay!!
			        		
			        		MediaPlayer tempMediaPlayer = new MediaPlayer();
			        		tempMediaPlayer.setOnCompletionListener(AudioPlayerManager.this);
			        		tempMediaPlayer.setDataSource(new FileInputStream(cachedFile).getFD());
			        		tempMediaPlayer.prepare();
			        		
			        		synchronized (mLock) {
			        			mMediaPlayer.pause();
				        		int position = mMediaPlayer.getCurrentPosition();
				        		tempMediaPlayer.seekTo(position);
				        		
				        		tempMediaPlayer.start();
				        		mMediaPlayer.release();
				        		mMediaPlayer = tempMediaPlayer;
			        		}
			        		
			        	}
		        	}
		        	 
		        	
		        
			        
			        inputStream.close();
			        outputStream.close();
			        
			        
				} catch (Exception e) {
					e.printStackTrace();
				}	
			}
			
		}).start();
		
		
	}

	@Override
	public void onCompletion(MediaPlayer mp) {
		
		if (mp == mMediaPlayer) {
			if (getRepeatMode() == REPEAT_ALL) {
				try {
					playNextAudio();
				} catch (Exception e) {
					e.printStackTrace();
				}
				
			} else {
				
				synchronized (mLock) {
					mMediaPlayer.seekTo(0);
					mMediaPlayer.start();
				}
			}
		}
	}
	
	public void interrupt() {
		
		synchronized (mLock) {
			if (mMediaPlayer != null && mMediaPlayer.isPlaying()) {
				
				mMediaPlayer.pause();
				mInterruptPaused = true;
			} else {
				mInterruptPaused = false;
			}
		}
	}
	
	public void resumeFromInterrupt() {
		synchronized (mLock) {
			if (mInterruptPaused) {
				mMediaPlayer.start();
				mInterruptPaused = false;
			}
		}
		
	}

}
